{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff734a78",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | default_exp _components.asyncapi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3c8cd28",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "import json\n",
    "import platform\n",
    "import shutil\n",
    "import subprocess  # nosec: B404: Consider possible security implications associated with the subprocess module.\n",
    "import tempfile\n",
    "from datetime import timedelta\n",
    "from enum import Enum\n",
    "from pathlib import Path\n",
    "from typing import *\n",
    "\n",
    "from pydantic import ConfigDict, BaseModel, Field, HttpUrl, model_serializer\n",
    "from pydantic.type_adapter import TypeAdapter\n",
    "\n",
    "from fastkafka._components.aiokafka_consumer_loop import ConsumeCallable\n",
    "from fastkafka._components.docs_dependencies import _check_npm_with_local\n",
    "from fastkafka._components.helpers import unwrap_list_type\n",
    "from fastkafka._components.logger import get_logger\n",
    "from fastkafka._components.producer_decorator import (\n",
    "    ProduceCallable,\n",
    "    unwrap_from_kafka_event,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16ea81bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "\n",
    "import pytest\n",
    "from pydantic import EmailStr\n",
    "from rich.pretty import pprint\n",
    "\n",
    "from fastkafka._components.aiokafka_consumer_loop import EventMetadata\n",
    "from fastkafka._components.logger import suppress_timestamps\n",
    "from fastkafka._components.producer_decorator import KafkaEvent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b33a28e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "logger = get_logger(__name__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41eb7517",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO] __main__: ok\n"
     ]
    }
   ],
   "source": [
    "suppress_timestamps()\n",
    "logger = get_logger(__name__)\n",
    "logger.info(\"ok\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a1c3419",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "class KafkaMessage(BaseModel):\n",
    "    # This following config is used to properly format timedelta in ISO 8601 format\n",
    "    model_config = ConfigDict(ser_json_timedelta=\"iso8601\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb0c4051",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">MyMsg</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">dt</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">datetime</span><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">.datetime</span><span style=\"font-weight: bold\">(</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2021</span>, <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">12</span>, <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">31</span>, <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">23</span>, <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">59</span>, <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">58</span><span style=\"font-weight: bold\">)</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">td</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">datetime</span><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">.timedelta</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">days</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>, <span style=\"color: #808000; text-decoration-color: #808000\">seconds</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">43321</span>, <span style=\"color: #808000; text-decoration-color: #808000\">microseconds</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">234568</span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mMyMsg\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mdt\u001b[0m=\u001b[1;35mdatetime\u001b[0m\u001b[1;35m.datetime\u001b[0m\u001b[1m(\u001b[0m\u001b[1;36m2021\u001b[0m, \u001b[1;36m12\u001b[0m, \u001b[1;36m31\u001b[0m, \u001b[1;36m23\u001b[0m, \u001b[1;36m59\u001b[0m, \u001b[1;36m58\u001b[0m\u001b[1m)\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mtd\u001b[0m=\u001b[1;35mdatetime\u001b[0m\u001b[1;35m.timedelta\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdays\u001b[0m=\u001b[1;36m1\u001b[0m, \u001b[33mseconds\u001b[0m=\u001b[1;36m43321\u001b[0m, \u001b[33mmicroseconds\u001b[0m=\u001b[1;36m234568\u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "\n",
    "class MyMsg(KafkaMessage):\n",
    "    dt: datetime = Field(..., example=datetime.now())\n",
    "    td: timedelta = Field(timedelta(days=1, hours=12, minutes=2, seconds=1.2345678))\n",
    "\n",
    "\n",
    "my_msg = MyMsg(dt=datetime(year=2021, month=12, day=31, hour=23, minute=59, second=58))\n",
    "pprint(my_msg)\n",
    "expected = '{\"dt\":\"2021-12-31T23:59:58\",\"td\":\"P1DT43321.234568S\"}'\n",
    "actual = my_msg.model_dump_json()\n",
    "assert actual == expected, f\"{actual} != {expected}\"\n",
    "\n",
    "actual = MyMsg.model_validate_json(actual)\n",
    "assert actual == my_msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d622ee56",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "class SecurityType(str, Enum):\n",
    "    plain = \"plain\"\n",
    "    userPassword = \"userPassword\"\n",
    "    apiKey = \"apiKey\"\n",
    "    X509 = \"X509\"\n",
    "    symmetricEncryption = \"symmetricEncryption\"\n",
    "    asymmetricEncryption = \"asymmetricEncryption\"\n",
    "    httpApiKey = \"httpApiKey\"\n",
    "    http = \"http\"\n",
    "    oauth2 = \"oauth2\"\n",
    "    openIdConnect = \"openIdConnect\"\n",
    "    scramSha256 = \"scramSha256\"\n",
    "    scramSha512 = \"scramSha512\"\n",
    "    gssapi = \"gssapi\"\n",
    "\n",
    "\n",
    "class APIKeyLocation(str, Enum):\n",
    "    user = \"user\"\n",
    "    password = \"password\"  # nosec\n",
    "    query = \"query\"\n",
    "    header = \"header\"\n",
    "    cookie = \"cookie\"\n",
    "\n",
    "\n",
    "sec_scheme_name_mapping = {\"security_type\": \"type\", \"api_key_loc\": \"in\"}\n",
    "\n",
    "\n",
    "class SecuritySchema(BaseModel):\n",
    "    security_type: SecurityType = Field(..., example=\"plain\")\n",
    "    description: Optional[str] = Field(None, example=\"My security scheme\")\n",
    "    name: Optional[str] = Field(None, example=\"my_secret_scheme\")\n",
    "    api_key_loc: Optional[APIKeyLocation] = Field(None, example=\"user\")\n",
    "    scheme: Optional[str] = None\n",
    "    bearerFormat: Optional[str] = None\n",
    "    flows: Optional[str] = None\n",
    "    openIdConnectUrl: Optional[str] = None\n",
    "\n",
    "    def __init__(self, **kwargs: Any):\n",
    "        for k, v in sec_scheme_name_mapping.items():\n",
    "            if v in kwargs:\n",
    "                kwargs[k] = kwargs.pop(v)\n",
    "        super().__init__(**kwargs)\n",
    "\n",
    "    def model_dump(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:\n",
    "        \"\"\"Renames internal names of members ('security_type' -> 'type', 'api_key_loc' -> 'in')\"\"\"\n",
    "        d = super().model_dump(*args, **kwargs)\n",
    "\n",
    "        for k, v in sec_scheme_name_mapping.items():\n",
    "            d[v] = d.pop(k)\n",
    "\n",
    "        # removes None values\n",
    "        d = {k: v for k, v in d.items() if v is not None}\n",
    "\n",
    "        return d\n",
    "\n",
    "    def model_dump_json(self, *args: Any, **kwargs: Any) -> str:\n",
    "        \"\"\"Serialize into JSON using model_dump()\"\"\"\n",
    "        return json.dumps(self.model_dump(), *args, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96810c7c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">SecuritySchema</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">security_type</span>=<span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">SecurityType.plain:</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008000; text-decoration-color: #008000\">'plain'</span><span style=\"font-weight: bold\">&gt;</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">api_key_loc</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">scheme</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">bearerFormat</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">flows</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">openIdConnectUrl</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mSecuritySchema\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33msecurity_type\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mSecurityType.plain:\u001b[0m\u001b[39m \u001b[0m\u001b[32m'plain'\u001b[0m\u001b[1m>\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mname\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mapi_key_loc\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mscheme\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mbearerFormat\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mflows\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mopenIdConnectUrl\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "JSON={\"type\": \"plain\"}\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">SecuritySchema</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">security_type</span>=<span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">SecurityType.plain:</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008000; text-decoration-color: #008000\">'plain'</span><span style=\"font-weight: bold\">&gt;</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">api_key_loc</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">scheme</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">bearerFormat</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">flows</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">openIdConnectUrl</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mSecuritySchema\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33msecurity_type\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mSecurityType.plain:\u001b[0m\u001b[39m \u001b[0m\u001b[32m'plain'\u001b[0m\u001b[1m>\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mname\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mapi_key_loc\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mscheme\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mbearerFormat\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mflows\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mopenIdConnectUrl\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "sec_schema = SecuritySchema(type=\"plain\")\n",
    "pprint(sec_schema)\n",
    "\n",
    "actual = sec_schema.model_dump_json()\n",
    "print(f\"JSON={actual}\")\n",
    "assert actual == '{\"type\": \"plain\"}', actual\n",
    "\n",
    "actual = SecuritySchema.model_validate_json(sec_schema.model_dump_json())\n",
    "pprint(actual)\n",
    "assert actual == sec_schema"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f9fe7e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "class KafkaBroker(BaseModel):\n",
    "    \"\"\"Kafka broker\"\"\"\n",
    "\n",
    "    url: str = Field(..., example=\"localhost\")\n",
    "    description: str = Field(\"Kafka broker\")\n",
    "    port: Union[str, int] = Field(\"9092\")\n",
    "    protocol: str = Field(\"kafka\")\n",
    "    security: Optional[SecuritySchema] = None\n",
    "\n",
    "    def model_dump(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:\n",
    "        \"\"\"Makes port a variable and remove it from the dictionary\"\"\"\n",
    "        d = super().model_dump(*args, **kwargs)\n",
    "        if self.security:\n",
    "            d[\"security\"] = self.security.model_dump(*args, **kwargs)\n",
    "        d[\"variables\"] = {\"port\": {\"default\": str(self.port)}}\n",
    "        d.pop(\"port\")\n",
    "\n",
    "        d = {k: v for k, v in d.items() if v is not None}\n",
    "\n",
    "        return d\n",
    "\n",
    "    def model_dump_json(self, *args: Any, **kwargs: Any) -> str:\n",
    "        \"\"\"Serialize into JSON using dict()\"\"\"\n",
    "        return json.dumps(self.model_dump(), *args, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d6f50fd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBroker</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">port</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">protocol</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">security</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span><span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mKafkaBroker\u001b[0m\u001b[1m(\u001b[0m\u001b[33murl\u001b[0m=\u001b[32m'kafka'\u001b[0m, \u001b[33mdescription\u001b[0m=\u001b[32m'Kafka broker'\u001b[0m, \u001b[33mport\u001b[0m=\u001b[32m'9092'\u001b[0m, \u001b[33mprotocol\u001b[0m=\u001b[32m'kafka'\u001b[0m, \u001b[33msecurity\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka\", \"variables\": {\"port\": {\"default\": \"9092\"}}}\n"
     ]
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "kafka_broker = KafkaBroker(url=\"kafka\")\n",
    "pprint(kafka_broker)\n",
    "\n",
    "expected = '{\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka\", \"variables\": {\"port\": {\"default\": \"9092\"}}}'\n",
    "print(kafka_broker.model_dump_json())\n",
    "assert kafka_broker.model_dump_json() == expected\n",
    "\n",
    "# serialization/deserialization test\n",
    "actual = KafkaBroker.model_validate_json(kafka_broker.model_dump_json())\n",
    "assert actual == kafka_broker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b635ef47",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBroker</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">port</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">protocol</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka-secure'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">security</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">SecuritySchema</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">security_type</span>=<span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">SecurityType.plain:</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008000; text-decoration-color: #008000\">'plain'</span><span style=\"font-weight: bold\">&gt;</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">api_key_loc</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">scheme</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">bearerFormat</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">flows</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">openIdConnectUrl</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mKafkaBroker\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33murl\u001b[0m=\u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mport\u001b[0m=\u001b[32m'9092'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mprotocol\u001b[0m=\u001b[32m'kafka-secure'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33msecurity\u001b[0m=\u001b[1;35mSecuritySchema\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[33msecurity_type\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mSecurityType.plain:\u001b[0m\u001b[39m \u001b[0m\u001b[32m'plain'\u001b[0m\u001b[1m>\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[33mname\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[33mapi_key_loc\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[33mscheme\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[33mbearerFormat\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[33mflows\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[33mopenIdConnectUrl\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "JSON={\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka-secure\", \"security\": {\"type\": \"plain\"}, \"variables\": {\"port\": {\"default\": \"9092\"}}}\n"
     ]
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "sec_kafka_broker = KafkaBroker(\n",
    "    url=\"kafka\", protocol=\"kafka-secure\", security=SecuritySchema(type=\"plain\")\n",
    ")\n",
    "pprint(sec_kafka_broker)\n",
    "\n",
    "expected = '{\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka-secure\", \"security\": {\"type\": \"plain\"}, \"variables\": {\"port\": {\"default\": \"9092\"}}}'\n",
    "actual = sec_kafka_broker.model_dump_json()\n",
    "print(f\"JSON={actual}\")\n",
    "assert actual == expected\n",
    "\n",
    "# serialization/deserialization test\n",
    "actual = KafkaBroker.model_validate_json(sec_kafka_broker.model_dump_json())\n",
    "assert actual == sec_kafka_broker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9375b58",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "class ContactInfo(BaseModel):\n",
    "    name: str = Field(..., example=\"My company\")\n",
    "    url: HttpUrl = Field(..., example=\"https://www.github.com/mycompany\")\n",
    "    email: str = Field(..., example=\"noreply@mycompany.com\")\n",
    "\n",
    "\n",
    "class KafkaServiceInfo(BaseModel):\n",
    "    title: str = Field(\"Title\")\n",
    "    version: str = Field(\"0.0.1\")\n",
    "    description: str = Field(\"Description of the service\")\n",
    "    contact: ContactInfo = Field(\n",
    "        ...,\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3feb80d0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaServiceInfo</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">title</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Title'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">version</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'0.0.1'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Description of the service'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">contact</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">ContactInfo</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'ACME'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">Url</span><span style=\"font-weight: bold\">(</span><span style=\"color: #008000; text-decoration-color: #008000\">'https://www.acme.com/'</span><span style=\"font-weight: bold\">)</span>, <span style=\"color: #808000; text-decoration-color: #808000\">email</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'noreply@acme.com'</span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mKafkaServiceInfo\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mtitle\u001b[0m=\u001b[32m'Title'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mversion\u001b[0m=\u001b[32m'0.0.1'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Description of the service'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mcontact\u001b[0m=\u001b[1;35mContactInfo\u001b[0m\u001b[1m(\u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'ACME'\u001b[0m, \u001b[33murl\u001b[0m=\u001b[1;35mUrl\u001b[0m\u001b[1m(\u001b[0m\u001b[32m'https://www.acme.com/'\u001b[0m\u001b[1m)\u001b[0m, \u001b[33memail\u001b[0m=\u001b[32m'noreply@acme.com'\u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "my_contact = ContactInfo(\n",
    "    name=\"ACME\", url=\"https://www.acme.com\", email=\"noreply@acme.com\"\n",
    ")\n",
    "service_info = KafkaServiceInfo(contact=my_contact)\n",
    "pprint(service_info)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e19f636c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "class KafkaBrokers(BaseModel):\n",
    "    brokers: Dict[str, Union[List[KafkaBroker], KafkaBroker]]\n",
    "\n",
    "    def model_dump(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:\n",
    "        \"\"\"Transcribe brokers into bootstrap server groups\"\"\"\n",
    "        d = super().model_dump(*args, **kwargs)\n",
    "\n",
    "        brokers = {}\n",
    "        for k, v in self.brokers.items():\n",
    "            if isinstance(v, list):\n",
    "                brokers.update(\n",
    "                    {f\"{k}-bootstrap-server-{i}\": u_v.model_dump() for i, u_v in enumerate(v)}\n",
    "                )\n",
    "            else:\n",
    "                brokers.update({f\"{k}\": v.model_dump()})\n",
    "        d[\"brokers\"] = brokers\n",
    "        d = {k: v for k, v in d.items() if v is not None}\n",
    "\n",
    "        return d\n",
    "\n",
    "    def model_dump_json(self, *args: Any, **kwargs: Any) -> str:\n",
    "        \"\"\"Serialize into JSON using dict()\"\"\"\n",
    "        return json.dumps(self.model_dump(), *args, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c08cc142",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBrokers</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">brokers</span>=<span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'dev'</span>: <span style=\"font-weight: bold\">[</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBroker</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">port</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">protocol</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">security</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'staging'</span>: <span style=\"font-weight: bold\">[</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBroker</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">port</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">protocol</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka-secure'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">security</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">SecuritySchema</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">security_type</span>=<span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">SecurityType.plain:</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008000; text-decoration-color: #008000\">'plain'</span><span style=\"font-weight: bold\">&gt;</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">api_key_loc</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">scheme</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">bearerFormat</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">flows</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">openIdConnectUrl</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">]</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mKafkaBrokers\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mbrokers\u001b[0m=\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'dev'\u001b[0m: \u001b[1m[\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1;35mKafkaBroker\u001b[0m\u001b[1m(\u001b[0m\u001b[33murl\u001b[0m=\u001b[32m'kafka'\u001b[0m, \u001b[33mdescription\u001b[0m=\u001b[32m'Kafka broker'\u001b[0m, \u001b[33mport\u001b[0m=\u001b[32m'9092'\u001b[0m, \u001b[33mprotocol\u001b[0m=\u001b[32m'kafka'\u001b[0m, \u001b[33msecurity\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'staging'\u001b[0m: \u001b[1m[\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1;35mKafkaBroker\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33murl\u001b[0m=\u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mport\u001b[0m=\u001b[32m'9092'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mprotocol\u001b[0m=\u001b[32m'kafka-secure'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33msecurity\u001b[0m=\u001b[1;35mSecuritySchema\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[33msecurity_type\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mSecurityType.plain:\u001b[0m\u001b[39m \u001b[0m\u001b[32m'plain'\u001b[0m\u001b[1m>\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[33mname\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[33mapi_key_loc\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[33mscheme\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[33mbearerFormat\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[33mflows\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[33mopenIdConnectUrl\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m]\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "JSON={\"brokers\": {\"dev-bootstrap-server-0\": {\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka\", \"variables\": {\"port\": {\"default\": \"9092\"}}}, \"staging-bootstrap-server-0\": {\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka-secure\", \"security\": {\"type\": \"plain\"}, \"variables\": {\"port\": {\"default\": \"9092\"}}}}}\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBrokers</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">brokers</span>=<span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'dev-bootstrap-server-0'</span>: <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBroker</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">port</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">protocol</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">security</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">)</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'staging-bootstrap-server-0'</span>: <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBroker</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">port</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">protocol</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka-secure'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">security</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">SecuritySchema</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">security_type</span>=<span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">SecurityType.plain:</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008000; text-decoration-color: #008000\">'plain'</span><span style=\"font-weight: bold\">&gt;</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">api_key_loc</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">scheme</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">bearerFormat</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">flows</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">openIdConnectUrl</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mKafkaBrokers\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mbrokers\u001b[0m=\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'dev-bootstrap-server-0'\u001b[0m: \u001b[1;35mKafkaBroker\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33murl\u001b[0m=\u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mport\u001b[0m=\u001b[32m'9092'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mprotocol\u001b[0m=\u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33msecurity\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m)\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'staging-bootstrap-server-0'\u001b[0m: \u001b[1;35mKafkaBroker\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33murl\u001b[0m=\u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mport\u001b[0m=\u001b[32m'9092'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mprotocol\u001b[0m=\u001b[32m'kafka-secure'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33msecurity\u001b[0m=\u001b[1;35mSecuritySchema\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33msecurity_type\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mSecurityType.plain:\u001b[0m\u001b[39m \u001b[0m\u001b[32m'plain'\u001b[0m\u001b[1m>\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mname\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mapi_key_loc\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mscheme\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mbearerFormat\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mflows\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mopenIdConnectUrl\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "kafka_brokers = KafkaBrokers(brokers={\"dev\": [kafka_broker], \"staging\": [sec_kafka_broker]})\n",
    "pprint(kafka_brokers)\n",
    "\n",
    "expected = '{\"brokers\": {\"dev-bootstrap-server-0\": {\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka\", \"variables\": {\"port\": {\"default\": \"9092\"}}}, \"staging-bootstrap-server-0\": {\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka-secure\", \"security\": {\"type\": \"plain\"}, \"variables\": {\"port\": {\"default\": \"9092\"}}}}}'\n",
    "\n",
    "actual = kafka_brokers.model_dump_json()\n",
    "print(f\"JSON={actual}\")\n",
    "assert actual == expected, actual\n",
    "\n",
    "actual = KafkaBrokers.model_validate_json(kafka_brokers.model_dump_json())\n",
    "pprint(actual)\n",
    "# assert actual == kafka_brokers\n",
    "assert actual.model_dump_json() == kafka_brokers.model_dump_json()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d04f1234",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_133/2021612751.py:4: PydanticDeprecatedSince20: The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, otherwise load the data then use `model_validate` instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.0/migration/\n",
      "  kafka_brokers = KafkaBrokers.parse_raw(brokers_json)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBrokers</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">brokers</span>=<span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'dev'</span>: <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBroker</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">port</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">protocol</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">security</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span><span style=\"font-weight: bold\">)</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'staging'</span>: <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaBroker</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">port</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">protocol</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'kafka-secure'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">security</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">SecuritySchema</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">security_type</span>=<span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">SecurityType.plain:</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008000; text-decoration-color: #008000\">'plain'</span><span style=\"font-weight: bold\">&gt;</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">api_key_loc</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">scheme</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">bearerFormat</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">flows</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #808000; text-decoration-color: #808000\">openIdConnectUrl</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mKafkaBrokers\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mbrokers\u001b[0m=\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'dev'\u001b[0m: \u001b[1;35mKafkaBroker\u001b[0m\u001b[1m(\u001b[0m\u001b[33murl\u001b[0m=\u001b[32m'kafka'\u001b[0m, \u001b[33mdescription\u001b[0m=\u001b[32m'Kafka broker'\u001b[0m, \u001b[33mport\u001b[0m=\u001b[32m'9092'\u001b[0m, \u001b[33mprotocol\u001b[0m=\u001b[32m'kafka'\u001b[0m, \u001b[33msecurity\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'staging'\u001b[0m: \u001b[1;35mKafkaBroker\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33murl\u001b[0m=\u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mport\u001b[0m=\u001b[32m'9092'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mprotocol\u001b[0m=\u001b[32m'kafka-secure'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[33msecurity\u001b[0m=\u001b[1;35mSecuritySchema\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33msecurity_type\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mSecurityType.plain:\u001b[0m\u001b[39m \u001b[0m\u001b[32m'plain'\u001b[0m\u001b[1m>\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mname\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mapi_key_loc\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mscheme\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mbearerFormat\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mflows\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[33mopenIdConnectUrl\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">KafkaServiceInfo</span><span style=\"font-weight: bold\">(</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">title</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Title'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">version</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'0.0.1'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">description</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'Description of the service'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #808000; text-decoration-color: #808000\">contact</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">ContactInfo</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'ACME'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">url</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">Url</span><span style=\"font-weight: bold\">(</span><span style=\"color: #008000; text-decoration-color: #008000\">'https://www.acme.com/'</span><span style=\"font-weight: bold\">)</span>, <span style=\"color: #808000; text-decoration-color: #808000\">email</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'noreply@acme.com'</span><span style=\"font-weight: bold\">)</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mKafkaServiceInfo\u001b[0m\u001b[1m(\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mtitle\u001b[0m=\u001b[32m'Title'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mversion\u001b[0m=\u001b[32m'0.0.1'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Description of the service'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[33mcontact\u001b[0m=\u001b[1;35mContactInfo\u001b[0m\u001b[1m(\u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'ACME'\u001b[0m, \u001b[33murl\u001b[0m=\u001b[1;35mUrl\u001b[0m\u001b[1m(\u001b[0m\u001b[32m'https://www.acme.com/'\u001b[0m\u001b[1m)\u001b[0m, \u001b[33memail\u001b[0m=\u001b[32m'noreply@acme.com'\u001b[0m\u001b[1m)\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'consumers'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_1'</span>: <span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">function</span><span style=\"color: #000000; text-decoration-color: #000000\"> on_my_topic_one at </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0x7f1e585d4540</span><span style=\"color: #000000; text-decoration-color: #000000\">&gt;,</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_2'</span><span style=\"color: #000000; text-decoration-color: #000000\">: &lt;function on_my_topic_2 at </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0x7f1e585d7380</span><span style=\"color: #000000; text-decoration-color: #000000\">&gt;</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #000000; text-decoration-color: #000000; font-weight: bold\">}</span><span style=\"color: #000000; text-decoration-color: #000000\">,</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'producers'</span><span style=\"color: #000000; text-decoration-color: #000000\">: </span><span style=\"color: #000000; text-decoration-color: #000000; font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_3'</span><span style=\"color: #000000; text-decoration-color: #000000\">: &lt;function to_my_topic_3 at </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0x7f1e585d7420</span><span style=\"color: #000000; text-decoration-color: #000000\">&gt;,</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_4'</span><span style=\"color: #000000; text-decoration-color: #000000\">: &lt;function to_my_topic_4 at </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0x7f1e585d74c0</span><span style=\"color: #000000; text-decoration-color: #000000\">&gt;,</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_5'</span><span style=\"color: #000000; text-decoration-color: #000000\">: &lt;function to_my_topic_5 at </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0x7f1e585d7560</span><span style=\"font-weight: bold\">&gt;</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"font-weight: bold\">}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'consumers'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_1'\u001b[0m: \u001b[1m<\u001b[0m\u001b[1;95mfunction\u001b[0m\u001b[39m on_my_topic_one at \u001b[0m\u001b[1;36m0x7f1e585d4540\u001b[0m\u001b[39m>,\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_2'\u001b[0m\u001b[39m: <function on_my_topic_2 at \u001b[0m\u001b[1;36m0x7f1e585d7380\u001b[0m\u001b[39m>\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'producers'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_3'\u001b[0m\u001b[39m: <function to_my_topic_3 at \u001b[0m\u001b[1;36m0x7f1e585d7420\u001b[0m\u001b[39m>,\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_4'\u001b[0m\u001b[39m: <function to_my_topic_4 at \u001b[0m\u001b[1;36m0x7f1e585d74c0\u001b[0m\u001b[39m>,\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_5'\u001b[0m\u001b[39m: <function to_my_topic_5 at \u001b[0m\u001b[1;36m0x7f1e585d7560\u001b[0m\u001b[1m>\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "brokers_json = '{\"brokers\": {\"dev\": {\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka\", \"variables\": {\"port\": {\"default\": \"9092\"}}}, \"staging\": {\"url\": \"kafka\", \"description\": \"Kafka broker\", \"protocol\": \"kafka-secure\", \"security\": {\"type\": \"plain\"}, \"variables\": {\"port\": {\"default\": \"9092\"}}}}}'\n",
    "kafka_brokers = KafkaBrokers.parse_raw(brokers_json)\n",
    "pprint(kafka_brokers)\n",
    "\n",
    "my_contact = ContactInfo(\n",
    "    name=\"ACME\", url=\"https://www.acme.com\", email=\"noreply@acme.com\"\n",
    ")\n",
    "kafka_service_info = KafkaServiceInfo(contact=my_contact)\n",
    "pprint(kafka_service_info)\n",
    "\n",
    "\n",
    "class MyInfo(KafkaMessage):\n",
    "    mobile: str = Field(..., example=\"+385987654321\")\n",
    "    name: str = Field(..., example=\"James Bond\")\n",
    "\n",
    "\n",
    "class MyMsgUrl(KafkaMessage):\n",
    "    info: MyInfo = Field(..., example=dict(mobile=\"+385987654321\", name=\"James Bond\"))\n",
    "    url: HttpUrl = Field(..., example=\"https://sis.gov.uk/agents/007\")\n",
    "\n",
    "\n",
    "class MyMsgEmail(KafkaMessage):\n",
    "    msg_url: MyMsgUrl = Field(\n",
    "        ...,\n",
    "        example=dict(\n",
    "            info=dict(mobile=\"+385987654321\", name=\"James Bond\"),\n",
    "            url=\"https://sis.gov.uk/agents/007\",\n",
    "        ),\n",
    "    )\n",
    "    email: EmailStr = Field(..., example=\"agent-007@sis.gov.uk\")\n",
    "\n",
    "\n",
    "def on_my_topic_one(msg: MyMsgUrl) -> None:\n",
    "    raise NotImplemented\n",
    "\n",
    "\n",
    "async def on_my_topic_2(msg: MyMsgEmail) -> None:\n",
    "    raise NotImplemented\n",
    "\n",
    "\n",
    "async def on_my_topic_2_meta(msg: MyMsgEmail, meta: EventMetadata) -> None:\n",
    "    raise NotImplemented\n",
    "\n",
    "\n",
    "async def on_my_topic_2_batch(msg: List[MyMsgEmail]) -> None:\n",
    "    raise NotImplemented\n",
    "\n",
    "\n",
    "async def to_my_topic_3(msg) -> MyMsgUrl:\n",
    "    raise NotImplemented\n",
    "\n",
    "\n",
    "async def to_my_topic_4(msg) -> KafkaEvent[MyMsgEmail]:\n",
    "    raise NotImplemented\n",
    "\n",
    "\n",
    "async def to_my_topic_5(msg) -> KafkaEvent[List[MyMsgEmail]]:\n",
    "    raise NotImplemented\n",
    "\n",
    "\n",
    "consumers = {\"my_topic_1\": on_my_topic_one, \"my_topic_2\": on_my_topic_2}\n",
    "producers = {\n",
    "    \"my_topic_3\": to_my_topic_3,\n",
    "    \"my_topic_4\": to_my_topic_4,\n",
    "    \"my_topic_5\": to_my_topic_5,\n",
    "}\n",
    "\n",
    "pprint(dict(consumers=consumers, producers=producers))\n",
    "assert set(consumers.keys()) == set([\"my_topic_1\", \"my_topic_2\"])\n",
    "assert set(producers.keys()) == set([\"my_topic_3\", \"my_topic_4\", \"my_topic_5\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41800426",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "# T = TypeVar(\"T\")\n",
    "\n",
    "\n",
    "def _get_msg_cls_for_producer(f: ProduceCallable) -> Type[Any]:\n",
    "    types = get_type_hints(f)\n",
    "    return_type = types.pop(\"return\", type(None))\n",
    "    # @app.producer must define a return value\n",
    "    if return_type == type(None):\n",
    "        raise ValueError(\n",
    "            f\"Producer function must have a defined return value, got {return_type} as return value\"\n",
    "        )\n",
    "\n",
    "    return_type = unwrap_from_kafka_event(return_type)\n",
    "    return_type = unwrap_list_type(return_type)\n",
    "\n",
    "    if not hasattr(return_type, \"json\"):\n",
    "        raise ValueError(f\"Producer function return value must have json method\")\n",
    "    return return_type  # type: ignore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "629071b5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.MyMsgUrl"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "expected = MyMsgUrl\n",
    "actual = _get_msg_cls_for_producer(to_my_topic_3)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5e40210",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.MyMsgEmail"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "expected = MyMsgEmail\n",
    "actual = _get_msg_cls_for_producer(to_my_topic_4)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2df557a2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.MyMsgEmail"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "expected = MyMsgEmail\n",
    "actual = _get_msg_cls_for_producer(to_my_topic_5)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "544f8bc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def no_return(i: int):\n",
    "    pass\n",
    "\n",
    "\n",
    "with pytest.raises(ValueError) as e:\n",
    "    _get_msg_cls_for_producer(no_return)\n",
    "\n",
    "assert e.value.args == (\n",
    "    \"Producer function must have a defined return value, got <class 'NoneType'> as return value\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c428d88c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_msg_cls_for_consumer(f: ConsumeCallable) -> Type[Any]:\n",
    "    types = get_type_hints(f)\n",
    "    return_type = types.pop(\"return\", type(None))\n",
    "    types_list = list(types.values())\n",
    "    # @app.consumer does not return a value\n",
    "    if return_type != type(None):\n",
    "        raise ValueError(\n",
    "            f\"Consumer function cannot return any value, got {return_type}\"\n",
    "        )\n",
    "    # @app.consumer first consumer argument must be a msg which is a subclass of BaseModel\n",
    "    try:\n",
    "        msg_type = types_list[0]\n",
    "\n",
    "        msg_type = unwrap_list_type(msg_type)\n",
    "\n",
    "        if not issubclass(msg_type, BaseModel):\n",
    "            raise ValueError(\n",
    "                f\"Consumer function first param must be a BaseModel subclass msg, got {types_list}\"\n",
    "            )\n",
    "\n",
    "        return msg_type  # type: ignore\n",
    "\n",
    "    except IndexError:\n",
    "        raise ValueError(\n",
    "            f\"Consumer function first param must be a BaseModel subclass msg, got {types_list}\"\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb801415",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.MyMsgUrl"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "expected = MyMsgUrl\n",
    "actual = _get_msg_cls_for_consumer(on_my_topic_one)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d0625f2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.MyMsgEmail"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "expected = MyMsgEmail\n",
    "actual = _get_msg_cls_for_consumer(on_my_topic_2_meta)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d701a5c9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.MyMsgEmail"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "expected = MyMsgEmail\n",
    "actual = _get_msg_cls_for_consumer(on_my_topic_2_batch)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2bcf415f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def no_input():\n",
    "    pass\n",
    "\n",
    "\n",
    "with pytest.raises(ValueError) as e:\n",
    "    _get_msg_cls_for_consumer(no_input)\n",
    "\n",
    "assert e.value.args == (\n",
    "    \"Consumer function first param must be a BaseModel subclass msg, got []\",\n",
    ")\n",
    "\n",
    "\n",
    "def has_return(a: int) -> int:\n",
    "    pass\n",
    "\n",
    "\n",
    "with pytest.raises(ValueError) as e:\n",
    "    _get_msg_cls_for_consumer(has_return)\n",
    "\n",
    "assert e.value.args == (\"Consumer function cannot return any value, got <class 'int'>\",)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fea7bc8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# |export\n",
    "\n",
    "\n",
    "def _get_topic_dict(\n",
    "    f: Callable[[Any], Any],\n",
    "    direction: str = \"publish\",\n",
    ") -> Dict[str, Any]:\n",
    "    if not direction in [\"publish\", \"subscribe\"]:\n",
    "        raise ValueError(\n",
    "            f\"direction must be one of ['publish', 'subscribe'], but it is '{direction}'.\"\n",
    "        )\n",
    "\n",
    "    #     msg_cls = None\n",
    "\n",
    "    if direction == \"publish\":\n",
    "        msg_cls = _get_msg_cls_for_producer(f)\n",
    "    elif direction == \"subscribe\":\n",
    "        msg_cls = _get_msg_cls_for_consumer(f)\n",
    "\n",
    "    msg_schema = {\"message\": {\"$ref\": f\"#/components/messages/{msg_cls.__name__}\"}}\n",
    "    if hasattr(f, \"description\"):\n",
    "        msg_schema[\"description\"] = getattr(f, \"description\")\n",
    "    elif f.__doc__ is not None:\n",
    "        msg_schema[\"description\"] = f.__doc__  # type: ignore\n",
    "    return {direction: msg_schema}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51c6a834",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'subscribe'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}}}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\u001b[32m'subscribe'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "expected = {\"subscribe\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"}}}\n",
    "\n",
    "actual = _get_topic_dict(on_my_topic_2, \"subscribe\")\n",
    "pprint(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41194f50",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'publish'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}}}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\u001b[32m'publish'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "expected = {\"publish\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"}}}\n",
    "\n",
    "actual = _get_topic_dict(to_my_topic_4, \"publish\")\n",
    "pprint(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "94de7f46",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'publish'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Topic description'</span><span style=\"font-weight: bold\">}}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\u001b[32m'publish'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m, \u001b[32m'description'\u001b[0m: \u001b[32m'Topic description'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "expected = {\n",
    "    \"publish\": {\n",
    "        \"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"},\n",
    "        \"description\": \"Topic description\",\n",
    "    }\n",
    "}\n",
    "\n",
    "setattr(to_my_topic_4, \"description\", \"Topic description\")\n",
    "\n",
    "actual = _get_topic_dict(to_my_topic_4, \"publish\")\n",
    "pprint(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "765e4151",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_channels_schema(\n",
    "    consumers: Dict[str, ConsumeCallable],\n",
    "    producers: Dict[str, ProduceCallable],\n",
    ") -> Dict[str, Dict[str, Dict[str, Any]]]:\n",
    "    topics = {}\n",
    "    for ms, d in zip([consumers, producers], [\"subscribe\", \"publish\"]):\n",
    "        for topic, f in ms.items():  # type: ignore\n",
    "            topics[topic] = _get_topic_dict(f, d)\n",
    "    return topics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36d6ec40",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_1'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'subscribe'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgUrl'</span><span style=\"font-weight: bold\">}}}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_2'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'subscribe'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}}}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_3'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'publish'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgUrl'</span><span style=\"font-weight: bold\">}}}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_4'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'publish'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Topic description'</span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_5'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'publish'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}}}</span>\n",
       "<span style=\"font-weight: bold\">}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'my_topic_1'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'subscribe'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgUrl'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'my_topic_2'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'subscribe'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'my_topic_3'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'publish'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgUrl'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'my_topic_4'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'publish'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m, \u001b[32m'description'\u001b[0m: \u001b[32m'Topic description'\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'my_topic_5'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'publish'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "expected = {\n",
    "    \"my_topic_1\": {\n",
    "        \"subscribe\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgUrl\"}}\n",
    "    },\n",
    "    \"my_topic_2\": {\n",
    "        \"subscribe\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"}}\n",
    "    },\n",
    "    \"my_topic_3\": {\"publish\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgUrl\"}}},\n",
    "    \"my_topic_4\": {\n",
    "        \"publish\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"}, 'description': 'Topic description'}\n",
    "    },\n",
    "    \"my_topic_5\": {\n",
    "        \"publish\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"}}\n",
    "    },\n",
    "}\n",
    "actual = _get_channels_schema(consumers, producers)\n",
    "pprint(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0933f70b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_kafka_msg_classes(\n",
    "    consumers: Dict[str, ConsumeCallable],\n",
    "    producers: Dict[str, ProduceCallable],\n",
    ") -> Set[Type[BaseModel]]:\n",
    "    fc = [_get_msg_cls_for_consumer(consumer) for consumer in consumers.values()]\n",
    "    fp = [_get_msg_cls_for_producer(producer) for producer in producers.values()]\n",
    "    return set(fc + fp)\n",
    "\n",
    "\n",
    "def _get_kafka_msg_definitions(\n",
    "    consumers: Dict[str, ConsumeCallable],\n",
    "    producers: Dict[str, ProduceCallable],\n",
    ") -> Dict[str, Dict[str, Any]]:\n",
    "    msg_classes = _get_kafka_msg_classes(consumers, producers)\n",
    "    _, msg_definitions = TypeAdapter.json_schemas([(msg_cls, \"validation\", TypeAdapter(msg_cls)) for msg_cls in msg_classes])\n",
    "    return msg_definitions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1aa371c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "expected = {\n",
    "    \"$defs\": {\n",
    "        \"MyInfo\": {\n",
    "            \"properties\": {\n",
    "                \"mobile\": {\n",
    "                    \"example\": \"+385987654321\",\n",
    "                    \"title\": \"Mobile\",\n",
    "                    \"type\": \"string\",\n",
    "                },\n",
    "                \"name\": {\"example\": \"James Bond\", \"title\": \"Name\", \"type\": \"string\"},\n",
    "            },\n",
    "            \"required\": [\"mobile\", \"name\"],\n",
    "            \"title\": \"MyInfo\",\n",
    "            \"type\": \"object\",\n",
    "        },\n",
    "        \"MyMsgEmail\": {\n",
    "            \"properties\": {\n",
    "                \"msg_url\": {\n",
    "                    \"allOf\": [{\"$ref\": \"#/$defs/MyMsgUrl\"}],\n",
    "                    \"example\": {\n",
    "                        \"info\": {\"mobile\": \"+385987654321\", \"name\": \"James Bond\"},\n",
    "                        \"url\": \"https://sis.gov.uk/agents/007\",\n",
    "                    },\n",
    "                },\n",
    "                \"email\": {\n",
    "                    \"example\": \"agent-007@sis.gov.uk\",\n",
    "                    \"format\": \"email\",\n",
    "                    \"title\": \"Email\",\n",
    "                    \"type\": \"string\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\"msg_url\", \"email\"],\n",
    "            \"title\": \"MyMsgEmail\",\n",
    "            \"type\": \"object\",\n",
    "        },\n",
    "        \"MyMsgUrl\": {\n",
    "            \"properties\": {\n",
    "                \"info\": {\n",
    "                    \"allOf\": [{\"$ref\": \"#/$defs/MyInfo\"}],\n",
    "                    \"example\": {\"mobile\": \"+385987654321\", \"name\": \"James Bond\"},\n",
    "                },\n",
    "                \"url\": {\n",
    "                    \"example\": \"https://sis.gov.uk/agents/007\",\n",
    "                    \"format\": \"uri\",\n",
    "                    \"maxLength\": 2083,\n",
    "                    \"minLength\": 1,\n",
    "                    \"title\": \"Url\",\n",
    "                    \"type\": \"string\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\"info\", \"url\"],\n",
    "            \"title\": \"MyMsgUrl\",\n",
    "            \"type\": \"object\",\n",
    "        },\n",
    "    }\n",
    "}\n",
    "\n",
    "msg_definitions = _get_kafka_msg_definitions(consumers, producers)\n",
    "assert msg_definitions == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51b8eca0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_example(cls: Type[BaseModel]) -> BaseModel:\n",
    "    kwargs: Dict[str, Any] = {}\n",
    "    for k, v in cls.model_fields.items():\n",
    "        #         try:\n",
    "        if hasattr(v, \"json_schema_extra\") and \"example\" in v.json_schema_extra:  # type: ignore\n",
    "            example = v.json_schema_extra[\"example\"]  # type: ignore\n",
    "            kwargs[k] = example\n",
    "    #         except:\n",
    "    #             pass\n",
    "    return json.loads(cls(**kwargs).model_dump_json())  # type: ignore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "248bcba6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'agent-007@sis.gov.uk'</span>\n",
       "<span style=\"font-weight: bold\">}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'msg_url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'email'\u001b[0m: \u001b[32m'agent-007@sis.gov.uk'\u001b[0m\n",
       "\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "expected = {\n",
    "    \"msg_url\": {\n",
    "        \"info\": {\"name\": \"James Bond\", \"mobile\": \"+385987654321\"},\n",
    "        \"url\": \"https://sis.gov.uk/agents/007\",\n",
    "    },\n",
    "    \"email\": \"agent-007@sis.gov.uk\",\n",
    "}\n",
    "\n",
    "actual = _get_example(MyMsgEmail)\n",
    "pprint(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d9f7df99",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _add_example_to_msg_definitions(\n",
    "    msg_cls: Type[BaseModel], msg_schema: Dict[str, Dict[str, Any]]\n",
    ") -> None:\n",
    "    try:\n",
    "        example = _get_example(msg_cls)\n",
    "    except Exception as e:\n",
    "        example = None\n",
    "    if example is not None:\n",
    "        msg_schema[\"$defs\"][msg_cls.__name__][\"example\"] = example\n",
    "\n",
    "\n",
    "def _get_msg_definitions_with_examples(\n",
    "    consumers: Dict[str, ConsumeCallable],\n",
    "    producers: Dict[str, ProduceCallable],\n",
    ") -> Dict[str, Dict[str, Any]]:\n",
    "    msg_classes = _get_kafka_msg_classes(consumers, producers)\n",
    "    msg_schema: Dict[str, Dict[str, Any]]\n",
    "    _, msg_schema = TypeAdapter.json_schemas([(msg_cls, \"validation\", TypeAdapter(msg_cls)) for msg_cls in msg_classes])\n",
    "    for msg_cls in msg_classes:\n",
    "        _add_example_to_msg_definitions(msg_cls, msg_schema)\n",
    "    msg_schema = (\n",
    "        {k: {\"payload\": v} for k, v in msg_schema[\"$defs\"].items()}\n",
    "        if \"$defs\" in msg_schema\n",
    "        else {}\n",
    "    )\n",
    "\n",
    "    return msg_schema"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "841e0eb0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n",
      "{'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyInfo'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Mobile'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Name'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyInfo'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgEmail'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'allOf'</span>: <span style=\"font-weight: bold\">[{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/$defs/MyMsgUrl'</span><span style=\"font-weight: bold\">}]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'agent-007@sis.gov.uk'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'format'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Email'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'email'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgEmail'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'agent-007@sis.gov.uk'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgUrl'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'allOf'</span>: <span style=\"font-weight: bold\">[{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/$defs/MyInfo'</span><span style=\"font-weight: bold\">}]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'format'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'uri'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'maxLength'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2083</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'minLength'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Url'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'url'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgUrl'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"font-weight: bold\">}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'MyInfo'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Mobile'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'name'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'James Bond'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Name'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'mobile'\u001b[0m, \u001b[32m'name'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyInfo'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'MyMsgEmail'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'msg_url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'allOf'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/$defs/MyMsgUrl'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'email'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'agent-007@sis.gov.uk'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'format'\u001b[0m: \u001b[32m'email'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'Email'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'msg_url'\u001b[0m, \u001b[32m'email'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyMsgEmail'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'msg_url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'email'\u001b[0m: \u001b[32m'agent-007@sis.gov.uk'\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'MyMsgUrl'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'allOf'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/$defs/MyInfo'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'format'\u001b[0m: \u001b[32m'uri'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'maxLength'\u001b[0m: \u001b[1;36m2083\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'minLength'\u001b[0m: \u001b[1;36m1\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'Url'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'info'\u001b[0m, \u001b[32m'url'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyMsgUrl'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "expected = {\n",
    "    \"MyInfo\": {\n",
    "        \"payload\": {\n",
    "            \"properties\": {\n",
    "                \"mobile\": {\n",
    "                    \"example\": \"+385987654321\",\n",
    "                    \"title\": \"Mobile\",\n",
    "                    \"type\": \"string\",\n",
    "                },\n",
    "                \"name\": {\"example\": \"James Bond\", \"title\": \"Name\", \"type\": \"string\"},\n",
    "            },\n",
    "            \"required\": [\"mobile\", \"name\"],\n",
    "            \"title\": \"MyInfo\",\n",
    "            \"type\": \"object\",\n",
    "        }\n",
    "    },\n",
    "    \"MyMsgEmail\": {\n",
    "        \"payload\": {\n",
    "            \"properties\": {\n",
    "                \"msg_url\": {\n",
    "                    \"allOf\": [{\"$ref\": \"#/$defs/MyMsgUrl\"}],\n",
    "                    \"example\": {\n",
    "                        \"info\": {\"mobile\": \"+385987654321\", \"name\": \"James Bond\"},\n",
    "                        \"url\": \"https://sis.gov.uk/agents/007\",\n",
    "                    },\n",
    "                },\n",
    "                \"email\": {\n",
    "                    \"example\": \"agent-007@sis.gov.uk\",\n",
    "                    \"format\": \"email\",\n",
    "                    \"title\": \"Email\",\n",
    "                    \"type\": \"string\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\"msg_url\", \"email\"],\n",
    "            \"title\": \"MyMsgEmail\",\n",
    "            \"type\": \"object\",\n",
    "            \"example\": {\n",
    "                \"msg_url\": {\n",
    "                    \"info\": {\"mobile\": \"+385987654321\", \"name\": \"James Bond\"},\n",
    "                    \"url\": \"https://sis.gov.uk/agents/007\",\n",
    "                },\n",
    "                \"email\": \"agent-007@sis.gov.uk\",\n",
    "            },\n",
    "        }\n",
    "    },\n",
    "    \"MyMsgUrl\": {\n",
    "        \"payload\": {\n",
    "            \"properties\": {\n",
    "                \"info\": {\n",
    "                    \"allOf\": [{\"$ref\": \"#/$defs/MyInfo\"}],\n",
    "                    \"example\": {\"mobile\": \"+385987654321\", \"name\": \"James Bond\"},\n",
    "                },\n",
    "                \"url\": {\n",
    "                    \"example\": \"https://sis.gov.uk/agents/007\",\n",
    "                    \"format\": \"uri\",\n",
    "                    \"maxLength\": 2083,\n",
    "                    \"minLength\": 1,\n",
    "                    \"title\": \"Url\",\n",
    "                    \"type\": \"string\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\"info\", \"url\"],\n",
    "            \"title\": \"MyMsgUrl\",\n",
    "            \"type\": \"object\",\n",
    "            \"example\": {\n",
    "                \"info\": {\"mobile\": \"+385987654321\", \"name\": \"James Bond\"},\n",
    "                \"url\": \"https://sis.gov.uk/agents/007\",\n",
    "            },\n",
    "        }\n",
    "    },\n",
    "}\n",
    "\n",
    "\n",
    "actual = _get_msg_definitions_with_examples(consumers, producers)\n",
    "pprint(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37bfd1fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_security_schemes(kafka_brokers: KafkaBrokers) -> Dict[str, Any]:\n",
    "    security_schemes = {}\n",
    "    for key, broker in kafka_brokers.brokers.items():\n",
    "        if isinstance(broker, list):\n",
    "            kafka_broker = broker[0]\n",
    "        else:\n",
    "            kafka_broker = broker\n",
    "\n",
    "        if kafka_broker.security is not None:\n",
    "            security_schemes[f\"{key}_default_security\"] = json.loads(\n",
    "                kafka_broker.security.model_dump_json()\n",
    "            )\n",
    "    return security_schemes"
   ]
  },
  {
   "cell_type": "raw",
   "id": "13132265",
   "metadata": {},
   "source": [
    "expected = {\n",
    "    \"staging_default_security\": {\"type\": \"plain\"},\n",
    "    \"production_default_security\": {\"type\": \"plain\"},\n",
    "}\n",
    "\n",
    "brokers = KafkaBrokers(\n",
    "    brokers={\n",
    "        \"dev\": KafkaBroker(\n",
    "            url=\"localhost\",\n",
    "            description=\"dev\",\n",
    "            port=\"9092\",\n",
    "        )\n",
    "    }\n",
    ")\n",
    "\n",
    "display(brokers)\n",
    "\n",
    "actual = _get_security_schemes(brokers)\n",
    "pprint(actual)\n",
    "assert actual == expected, actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eeb0e316",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_components_schema(\n",
    "    consumers: Dict[str, ConsumeCallable],\n",
    "    producers: Dict[str, ProduceCallable],\n",
    "    kafka_brokers: KafkaBrokers,\n",
    ") -> Dict[str, Any]:\n",
    "    definitions = _get_msg_definitions_with_examples(consumers, producers)\n",
    "    msg_classes = [cls.__name__ for cls in _get_kafka_msg_classes(consumers, producers)]\n",
    "    components = {\n",
    "        \"messages\": {k: v for k, v in definitions.items() if k in msg_classes},\n",
    "        \"schemas\": {k: v for k, v in definitions.items() if k not in msg_classes},\n",
    "        \"securitySchemes\": _get_security_schemes(kafka_brokers),\n",
    "    }\n",
    "    substitutions = {\n",
    "        f\"#/$defs/{k}\": f\"#/components/messages/{k}\"\n",
    "        if k in msg_classes\n",
    "        else f\"#/components/schemas/{k}\"\n",
    "        for k in definitions.keys()\n",
    "    }\n",
    "\n",
    "    def _sub_values(d: Any, substitutions: Dict[str, str] = substitutions) -> Any:\n",
    "        if isinstance(d, dict):\n",
    "            d = {k: _sub_values(v) for k, v in d.items()}\n",
    "        if isinstance(d, list):\n",
    "            d = [_sub_values(k) for k in d]\n",
    "        elif isinstance(d, str):\n",
    "            for k, v in substitutions.items():\n",
    "                if d == k:\n",
    "                    d = v\n",
    "        return d\n",
    "\n",
    "    return _sub_values(components)  # type: ignore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df0128e3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n",
      "{'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'messages'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgEmail'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'allOf'</span>: <span style=\"font-weight: bold\">[{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgUrl'</span><span style=\"font-weight: bold\">}]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'agent-007@sis.gov.uk'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'format'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Email'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'email'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgEmail'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'agent-007@sis.gov.uk'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgUrl'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'allOf'</span>: <span style=\"font-weight: bold\">[{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/schemas/MyInfo'</span><span style=\"font-weight: bold\">}]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'format'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'uri'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'maxLength'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2083</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'minLength'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Url'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'url'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgUrl'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'schemas'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyInfo'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Mobile'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Name'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyInfo'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'securitySchemes'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'staging_default_security'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'plain'</span><span style=\"font-weight: bold\">}}</span>\n",
       "<span style=\"font-weight: bold\">}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'messages'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'MyMsgEmail'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'msg_url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'allOf'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgUrl'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'email'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'agent-007@sis.gov.uk'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'format'\u001b[0m: \u001b[32m'email'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'Email'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'msg_url'\u001b[0m, \u001b[32m'email'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyMsgEmail'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'msg_url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'email'\u001b[0m: \u001b[32m'agent-007@sis.gov.uk'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'MyMsgUrl'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'allOf'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/schemas/MyInfo'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'format'\u001b[0m: \u001b[32m'uri'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'maxLength'\u001b[0m: \u001b[1;36m2083\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'minLength'\u001b[0m: \u001b[1;36m1\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'Url'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'info'\u001b[0m, \u001b[32m'url'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyMsgUrl'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'schemas'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'MyInfo'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Mobile'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'name'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'James Bond'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Name'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'mobile'\u001b[0m, \u001b[32m'name'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyInfo'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'securitySchemes'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'staging_default_security'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'plain'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "components = _get_components_schema(consumers, producers, kafka_brokers)\n",
    "pprint(components)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4debb61",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_servers_schema(kafka_brokers: KafkaBrokers) -> Dict[str, Any]:\n",
    "    servers = json.loads(kafka_brokers.model_dump_json(sort_keys=False))[\"brokers\"]\n",
    "\n",
    "    for key, kafka_broker in servers.items():\n",
    "        if \"security\" in kafka_broker:\n",
    "            servers[key][\"security\"] = [{f\"{key}_default_security\": []}]\n",
    "    return servers  # type: ignore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "486f09df",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'dev'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'protocol'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'variables'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'port'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'default'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span><span style=\"font-weight: bold\">}}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'staging'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'protocol'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'kafka-secure'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'security'</span>: <span style=\"font-weight: bold\">[{</span><span style=\"color: #008000; text-decoration-color: #008000\">'staging_default_security'</span>: <span style=\"font-weight: bold\">[]}]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'variables'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'port'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'default'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span><span style=\"font-weight: bold\">}}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"font-weight: bold\">}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'dev'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'protocol'\u001b[0m: \u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'variables'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'port'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'default'\u001b[0m: \u001b[32m'9092'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'staging'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'protocol'\u001b[0m: \u001b[32m'kafka-secure'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'security'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'staging_default_security'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'variables'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'port'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'default'\u001b[0m: \u001b[32m'9092'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "expected = {\n",
    "    \"dev\": {\n",
    "        \"url\": \"kafka\",\n",
    "        \"description\": \"Kafka broker\",\n",
    "        \"protocol\": \"kafka\",\n",
    "        \"variables\": {\"port\": {\"default\": \"9092\"}},\n",
    "    },\n",
    "    \"staging\": {\n",
    "        \"url\": \"kafka\",\n",
    "        \"description\": \"Kafka broker\",\n",
    "        \"protocol\": \"kafka-secure\",\n",
    "        \"security\": [{\"staging_default_security\": []}],\n",
    "        \"variables\": {\"port\": {\"default\": \"9092\"}},\n",
    "    },\n",
    "}\n",
    "\n",
    "actual = _get_servers_schema(kafka_brokers)\n",
    "pprint(actual)\n",
    "assert actual == expected, actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65365b1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_asyncapi_schema(\n",
    "    consumers: Dict[str, ConsumeCallable],\n",
    "    producers: Dict[str, ProduceCallable],\n",
    "    kafka_brokers: KafkaBrokers,\n",
    "    kafka_service_info: KafkaServiceInfo,\n",
    ") -> Dict[str, Any]:\n",
    "    #     # we don't use dict because we need custom JSON encoders\n",
    "    info = json.loads(kafka_service_info.model_dump_json())\n",
    "    servers = _get_servers_schema(kafka_brokers)\n",
    "    #     # should be in the proper format already\n",
    "    channels = _get_channels_schema(consumers, producers)\n",
    "    components = _get_components_schema(consumers, producers, kafka_brokers)\n",
    "    return {\n",
    "        \"asyncapi\": \"2.5.0\",\n",
    "        \"info\": info,\n",
    "        \"servers\": servers,\n",
    "        \"channels\": channels,\n",
    "        \"components\": components,\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "52fa12e4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n",
      "{'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'asyncapi'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'2.5.0'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Title'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'version'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'0.0.1'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Description of the service'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'contact'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'ACME'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://www.acme.com/'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'noreply@acme.com'</span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'servers'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'dev'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'protocol'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'variables'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'port'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'default'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span><span style=\"font-weight: bold\">}}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'staging'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'kafka'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Kafka broker'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'protocol'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'kafka-secure'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'security'</span>: <span style=\"font-weight: bold\">[{</span><span style=\"color: #008000; text-decoration-color: #008000\">'staging_default_security'</span>: <span style=\"font-weight: bold\">[]}]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'variables'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'port'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'default'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'9092'</span><span style=\"font-weight: bold\">}}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'channels'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_1'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'subscribe'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgUrl'</span><span style=\"font-weight: bold\">}}}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_2'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'subscribe'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}}}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_3'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'publish'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgUrl'</span><span style=\"font-weight: bold\">}}}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_4'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'publish'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Topic description'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'my_topic_5'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'publish'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'message'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgEmail'</span><span style=\"font-weight: bold\">}}}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"color: #008000; text-decoration-color: #008000\">'components'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'messages'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgEmail'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'allOf'</span>: <span style=\"font-weight: bold\">[{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/messages/MyMsgUrl'</span><span style=\"font-weight: bold\">}]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'agent-007@sis.gov.uk'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'format'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Email'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'email'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgEmail'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'msg_url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'email'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'agent-007@sis.gov.uk'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgUrl'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'allOf'</span>: <span style=\"font-weight: bold\">[{</span><span style=\"color: #008000; text-decoration-color: #008000\">'$ref'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'#/components/schemas/MyInfo'</span><span style=\"font-weight: bold\">}]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'format'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'uri'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'maxLength'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2083</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'minLength'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Url'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'url'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyMsgUrl'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'info'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'url'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'https://sis.gov.uk/agents/007'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'schemas'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'MyInfo'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'payload'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'+385987654321'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Mobile'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'example'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'James Bond'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Name'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'mobile'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span><span style=\"font-weight: bold\">]</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'MyInfo'</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   │   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"font-weight: bold\">}</span>,\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   │   </span><span style=\"color: #008000; text-decoration-color: #008000\">'securitySchemes'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'staging_default_security'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'plain'</span><span style=\"font-weight: bold\">}}</span>\n",
       "<span style=\"color: #7fbf7f; text-decoration-color: #7fbf7f\">│   </span><span style=\"font-weight: bold\">}</span>\n",
       "<span style=\"font-weight: bold\">}</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'asyncapi'\u001b[0m: \u001b[32m'2.5.0'\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'Title'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'version'\u001b[0m: \u001b[32m'0.0.1'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Description of the service'\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'contact'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'name'\u001b[0m: \u001b[32m'ACME'\u001b[0m, \u001b[32m'url'\u001b[0m: \u001b[32m'https://www.acme.com/'\u001b[0m, \u001b[32m'email'\u001b[0m: \u001b[32m'noreply@acme.com'\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'servers'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'dev'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'protocol'\u001b[0m: \u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'variables'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'port'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'default'\u001b[0m: \u001b[32m'9092'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'staging'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'kafka'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Kafka broker'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'protocol'\u001b[0m: \u001b[32m'kafka-secure'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'security'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'staging_default_security'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'variables'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'port'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'default'\u001b[0m: \u001b[32m'9092'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'channels'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_1'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'subscribe'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgUrl'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_2'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'subscribe'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_3'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'publish'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgUrl'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_4'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'publish'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Topic description'\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'my_topic_5'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'publish'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'message'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgEmail'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   \u001b[0m\u001b[32m'components'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'messages'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'MyMsgEmail'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'msg_url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'allOf'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/messages/MyMsgUrl'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'email'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'agent-007@sis.gov.uk'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'format'\u001b[0m: \u001b[32m'email'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'Email'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'msg_url'\u001b[0m, \u001b[32m'email'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyMsgEmail'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'msg_url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'email'\u001b[0m: \u001b[32m'agent-007@sis.gov.uk'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'MyMsgUrl'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'allOf'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'$ref'\u001b[0m: \u001b[32m'#/components/schemas/MyInfo'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'format'\u001b[0m: \u001b[32m'uri'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'maxLength'\u001b[0m: \u001b[1;36m2083\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'minLength'\u001b[0m: \u001b[1;36m1\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'Url'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'info'\u001b[0m, \u001b[32m'url'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyMsgUrl'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'example'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'info'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'James Bond'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'url'\u001b[0m: \u001b[32m'https://sis.gov.uk/agents/007'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'schemas'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'MyInfo'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[32m'payload'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'mobile'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'+385987654321'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Mobile'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   │   \u001b[0m\u001b[32m'name'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'example'\u001b[0m: \u001b[32m'James Bond'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Name'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'mobile'\u001b[0m, \u001b[32m'name'\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'title'\u001b[0m: \u001b[32m'MyInfo'\u001b[0m,\n",
       "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m\n",
       "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
       "\u001b[2;32m│   │   \u001b[0m\u001b[32m'securitySchemes'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'staging_default_security'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'plain'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
       "\u001b[1m}\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# | output: false\n",
    "\n",
    "expected = {\n",
    "    \"asyncapi\": \"2.5.0\",\n",
    "    \"info\": {\n",
    "        \"title\": \"Title\",\n",
    "        \"version\": \"0.0.1\",\n",
    "        \"description\": \"Description of the service\",\n",
    "        \"contact\": {\n",
    "            \"name\": \"ACME\",\n",
    "            \"url\": \"https://www.acme.com/\",\n",
    "            \"email\": \"noreply@acme.com\",\n",
    "        },\n",
    "    },\n",
    "    \"servers\": {\n",
    "        \"dev\": {\n",
    "            \"url\": \"kafka\",\n",
    "            \"description\": \"Kafka broker\",\n",
    "            \"protocol\": \"kafka\",\n",
    "            \"variables\": {\"port\": {\"default\": \"9092\"}},\n",
    "        },\n",
    "        \"staging\": {\n",
    "            \"url\": \"kafka\",\n",
    "            \"description\": \"Kafka broker\",\n",
    "            \"protocol\": \"kafka-secure\",\n",
    "            \"security\": [{\"staging_default_security\": []}],\n",
    "            \"variables\": {\"port\": {\"default\": \"9092\"}},\n",
    "        },\n",
    "    },\n",
    "    \"channels\": {\n",
    "        \"my_topic_1\": {\n",
    "            \"subscribe\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgUrl\"}}\n",
    "        },\n",
    "        \"my_topic_2\": {\n",
    "            \"subscribe\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"}}\n",
    "        },\n",
    "        \"my_topic_3\": {\n",
    "            \"publish\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgUrl\"}}\n",
    "        },\n",
    "        \"my_topic_4\": {\n",
    "            \"publish\": {\n",
    "                \"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"},\n",
    "                \"description\": \"Topic description\",\n",
    "            }\n",
    "        },\n",
    "        \"my_topic_5\": {\n",
    "            \"publish\": {\"message\": {\"$ref\": \"#/components/messages/MyMsgEmail\"}}\n",
    "        },\n",
    "    },\n",
    "    \"components\": {\n",
    "        \"messages\": {\n",
    "            \"MyMsgEmail\": {\n",
    "                \"payload\": {\n",
    "                    \"properties\": {\n",
    "                        \"msg_url\": {\n",
    "                            \"allOf\": [{\"$ref\": \"#/components/messages/MyMsgUrl\"}],\n",
    "                            \"example\": {\n",
    "                                \"info\": {\n",
    "                                    \"mobile\": \"+385987654321\",\n",
    "                                    \"name\": \"James Bond\",\n",
    "                                },\n",
    "                                \"url\": \"https://sis.gov.uk/agents/007\",\n",
    "                            },\n",
    "                        },\n",
    "                        \"email\": {\n",
    "                            \"example\": \"agent-007@sis.gov.uk\",\n",
    "                            \"format\": \"email\",\n",
    "                            \"title\": \"Email\",\n",
    "                            \"type\": \"string\",\n",
    "                        },\n",
    "                    },\n",
    "                    \"required\": [\"msg_url\", \"email\"],\n",
    "                    \"title\": \"MyMsgEmail\",\n",
    "                    \"type\": \"object\",\n",
    "                    \"example\": {\n",
    "                        \"msg_url\": {\n",
    "                            \"info\": {\"mobile\": \"+385987654321\", \"name\": \"James Bond\"},\n",
    "                            \"url\": \"https://sis.gov.uk/agents/007\",\n",
    "                        },\n",
    "                        \"email\": \"agent-007@sis.gov.uk\",\n",
    "                    },\n",
    "                }\n",
    "            },\n",
    "            \"MyMsgUrl\": {\n",
    "                \"payload\": {\n",
    "                    \"properties\": {\n",
    "                        \"info\": {\n",
    "                            \"allOf\": [{\"$ref\": \"#/components/schemas/MyInfo\"}],\n",
    "                            \"example\": {\n",
    "                                \"mobile\": \"+385987654321\",\n",
    "                                \"name\": \"James Bond\",\n",
    "                            },\n",
    "                        },\n",
    "                        \"url\": {\n",
    "                            \"example\": \"https://sis.gov.uk/agents/007\",\n",
    "                            \"format\": \"uri\",\n",
    "                            \"maxLength\": 2083,\n",
    "                            \"minLength\": 1,\n",
    "                            \"title\": \"Url\",\n",
    "                            \"type\": \"string\",\n",
    "                        },\n",
    "                    },\n",
    "                    \"required\": [\"info\", \"url\"],\n",
    "                    \"title\": \"MyMsgUrl\",\n",
    "                    \"type\": \"object\",\n",
    "                    \"example\": {\n",
    "                        \"info\": {\"mobile\": \"+385987654321\", \"name\": \"James Bond\"},\n",
    "                        \"url\": \"https://sis.gov.uk/agents/007\",\n",
    "                    },\n",
    "                }\n",
    "            },\n",
    "        },\n",
    "        \"schemas\": {\n",
    "            \"MyInfo\": {\n",
    "                \"payload\": {\n",
    "                    \"properties\": {\n",
    "                        \"mobile\": {\n",
    "                            \"example\": \"+385987654321\",\n",
    "                            \"title\": \"Mobile\",\n",
    "                            \"type\": \"string\",\n",
    "                        },\n",
    "                        \"name\": {\n",
    "                            \"example\": \"James Bond\",\n",
    "                            \"title\": \"Name\",\n",
    "                            \"type\": \"string\",\n",
    "                        },\n",
    "                    },\n",
    "                    \"required\": [\"mobile\", \"name\"],\n",
    "                    \"title\": \"MyInfo\",\n",
    "                    \"type\": \"object\",\n",
    "                }\n",
    "            }\n",
    "        },\n",
    "        \"securitySchemes\": {\"staging_default_security\": {\"type\": \"plain\"}},\n",
    "    },\n",
    "}\n",
    "asyncapi_schema = _get_asyncapi_schema(\n",
    "    consumers, producers, kafka_brokers, kafka_service_info\n",
    ")\n",
    "pprint(asyncapi_schema)\n",
    "assert asyncapi_schema == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3df0720d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# |export\n",
    "\n",
    "\n",
    "def yaml_file_cmp(file_1: Union[Path, str], file_2: Union[Path, str]) -> bool:\n",
    "    \"\"\"Compares two YAML files and returns True if their contents are equal, False otherwise.\n",
    "\n",
    "    Args:\n",
    "        file_1: Path or string representing the first YAML file.\n",
    "        file_2: Path or string representing the second YAML file.\n",
    "\n",
    "    Returns:\n",
    "        A boolean indicating whether the contents of the two YAML files are equal.\n",
    "    \"\"\"\n",
    "    try:\n",
    "        import yaml\n",
    "    except Exception as e:\n",
    "        msg = \"Please install docs version of fastkafka using 'pip install fastkafka[docs]' command\"\n",
    "        logger.error(msg)\n",
    "        raise RuntimeError(msg)\n",
    "\n",
    "    def _read(f: Union[Path, str]) -> Dict[str, Any]:\n",
    "        with open(f) as stream:\n",
    "            return yaml.safe_load(stream)  # type: ignore\n",
    "\n",
    "    d = [_read(f) for f in [file_1, file_2]]\n",
    "    return d[0] == d[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1bd1bd2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _generate_async_spec(\n",
    "    *,\n",
    "    consumers: Dict[str, ConsumeCallable],\n",
    "    producers: Dict[str, ProduceCallable],\n",
    "    kafka_brokers: KafkaBrokers,\n",
    "    kafka_service_info: KafkaServiceInfo,\n",
    "    spec_path: Path,\n",
    "    force_rebuild: bool,\n",
    ") -> bool:\n",
    "    try:\n",
    "        import yaml\n",
    "    except Exception as e:\n",
    "        msg = \"Please install docs version of fastkafka using 'pip install fastkafka[docs]' command\"\n",
    "        logger.error(msg)\n",
    "        raise RuntimeError(msg)\n",
    "\n",
    "    # generate spec file\n",
    "    asyncapi_schema = _get_asyncapi_schema(\n",
    "        consumers, producers, kafka_brokers, kafka_service_info\n",
    "    )\n",
    "    if not spec_path.exists():\n",
    "        logger.info(\n",
    "            f\"Old async specifications at '{spec_path.resolve()}' does not exist.\"\n",
    "        )\n",
    "    spec_path.parent.mkdir(exist_ok=True, parents=True)\n",
    "    with tempfile.TemporaryDirectory() as d:\n",
    "        with open(Path(d) / \"asyncapi.yml\", \"w\") as f:\n",
    "            yaml.dump(asyncapi_schema, f, sort_keys=False)\n",
    "        spec_changed = not (\n",
    "            spec_path.exists() and yaml_file_cmp(Path(d) / \"asyncapi.yml\", spec_path)\n",
    "        )\n",
    "        if spec_changed or force_rebuild:\n",
    "            shutil.copyfile(Path(d) / \"asyncapi.yml\", spec_path)\n",
    "            logger.info(\n",
    "                f\"New async specifications generated at: '{spec_path.resolve()}'\"\n",
    "            )\n",
    "            return True\n",
    "        else:\n",
    "            logger.info(\n",
    "                f\"Keeping the old async specifications at: '{spec_path.resolve()}'\"\n",
    "            )\n",
    "            return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c91dc3a4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n",
      "{'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}\n",
      "[INFO] __main__: Old async specifications at '/tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml' does not exist.\n",
      "[INFO] __main__: New async specifications generated at: '/tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml'\n",
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n",
      "{'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}\n",
      "[INFO] __main__: Keeping the old async specifications at: '/tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml'\n",
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n",
      "{'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}\n",
      "[INFO] __main__: New async specifications generated at: '/tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml'\n"
     ]
    }
   ],
   "source": [
    "with tempfile.TemporaryDirectory() as d:\n",
    "    try:\n",
    "        asyncapi_path = Path(d).parent / \"003_AsyncAPI\" / \"asyncapi\"\n",
    "        if asyncapi_path.exists():\n",
    "            shutil.rmtree(asyncapi_path)\n",
    "        spec_path = Path(asyncapi_path) / \"spec\" / \"asyncapi.yml\"\n",
    "\n",
    "        is_spec_built = _generate_async_spec(\n",
    "            consumers=consumers,\n",
    "            producers=producers,\n",
    "            kafka_brokers=kafka_brokers,\n",
    "            kafka_service_info=kafka_service_info,\n",
    "            spec_path=spec_path,\n",
    "            force_rebuild=False,\n",
    "        )\n",
    "        assert is_spec_built\n",
    "        assert (Path(asyncapi_path) / \"spec\" / \"asyncapi.yml\").exists()\n",
    "\n",
    "        is_spec_built = _generate_async_spec(\n",
    "            consumers=consumers,\n",
    "            producers=producers,\n",
    "            kafka_brokers=kafka_brokers,\n",
    "            kafka_service_info=kafka_service_info,\n",
    "            spec_path=spec_path,\n",
    "            force_rebuild=False,\n",
    "        )\n",
    "        assert not is_spec_built\n",
    "        assert (Path(asyncapi_path) / \"spec\" / \"asyncapi.yml\").exists()\n",
    "\n",
    "        is_spec_built = _generate_async_spec(\n",
    "            consumers=consumers,\n",
    "            producers=producers,\n",
    "            kafka_brokers=kafka_brokers,\n",
    "            kafka_service_info=kafka_service_info,\n",
    "            spec_path=spec_path,\n",
    "            force_rebuild=True,\n",
    "        )\n",
    "        assert is_spec_built\n",
    "        assert (Path(asyncapi_path) / \"spec\" / \"asyncapi.yml\").exists()\n",
    "\n",
    "    finally:\n",
    "        shutil.rmtree(asyncapi_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffb0e6e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _generate_async_docs(\n",
    "    *,\n",
    "    spec_path: Path,\n",
    "    docs_path: Path,\n",
    ") -> None:\n",
    "    _check_npm_with_local()\n",
    "    cmd = [\n",
    "        \"npx\",\n",
    "        \"-y\",\n",
    "        \"-p\",\n",
    "        \"@asyncapi/generator\",\n",
    "        \"ag\",\n",
    "        f\"{spec_path}\",\n",
    "        \"@asyncapi/html-template\",\n",
    "        \"-o\",\n",
    "        f\"{docs_path}\",\n",
    "        \"--force-write\",\n",
    "    ]\n",
    "    # nosemgrep: python.lang.security.audit.subprocess-shell-true.subprocess-shell-true\n",
    "    p = subprocess.run(  # nosec: B602, B603 subprocess call - check for execution of untrusted input.\n",
    "        cmd,\n",
    "        stderr=subprocess.STDOUT,\n",
    "        stdout=subprocess.PIPE,\n",
    "        shell=True if platform.system() == \"Windows\" else False,\n",
    "    )\n",
    "    if p.returncode == 0:\n",
    "        logger.info(f\"Async docs generated at '{docs_path}'\")\n",
    "        logger.info(f\"Output of '$ {' '.join(cmd)}'{p.stdout.decode()}\")\n",
    "    else:\n",
    "        logger.error(f\"Generation of async docs failed!\")\n",
    "        logger.info(f\"Output of '$ {' '.join(cmd)}'{p.stdout.decode()}\")\n",
    "        raise ValueError(\n",
    "            f\"Generation of async docs failed, used '$ {' '.join(cmd)}'{p.stdout.decode()}\"\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "07f42332",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n",
      "{'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}\n",
      "[INFO] __main__: Old async specifications at '/tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml' does not exist.\n",
      "[INFO] __main__: New async specifications generated at: '/tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml'\n",
      "[INFO] __main__: Async docs generated at '/tmp/003_AsyncAPI/asyncapi/docs'\n",
      "[INFO] __main__: Output of '$ npx -y -p @asyncapi/generator ag /tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml @asyncapi/html-template -o /tmp/003_AsyncAPI/asyncapi/docs --force-write'\u001b[32m\n",
      "\n",
      "Done! ✨\u001b[0m\n",
      "\u001b[33mCheck out your shiny new generated files at \u001b[0m\u001b[35m/tmp/003_AsyncAPI/asyncapi/docs\u001b[0m\u001b[33m.\u001b[0m\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "with tempfile.TemporaryDirectory() as d:\n",
    "    try:\n",
    "        asyncapi_path = Path(d).parent / \"003_AsyncAPI\" / \"asyncapi\"\n",
    "        if asyncapi_path.exists():\n",
    "            shutil.rmtree(asyncapi_path)\n",
    "        spec_path = Path(asyncapi_path) / \"spec\" / \"asyncapi.yml\"\n",
    "        docs_path = Path(asyncapi_path) / \"docs\"\n",
    "\n",
    "        is_spec_built = _generate_async_spec(\n",
    "            consumers=consumers,\n",
    "            producers=producers,\n",
    "            kafka_brokers=kafka_brokers,\n",
    "            kafka_service_info=kafka_service_info,\n",
    "            spec_path=spec_path,\n",
    "            force_rebuild=False,\n",
    "        )\n",
    "\n",
    "        _generate_async_docs(\n",
    "            spec_path=spec_path,\n",
    "            docs_path=docs_path,\n",
    "        )\n",
    "        assert docs_path.exists()\n",
    "    finally:\n",
    "        shutil.rmtree(asyncapi_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "238947d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# |export\n",
    "\n",
    "\n",
    "def export_async_spec(\n",
    "    *,\n",
    "    consumers: Dict[str, ConsumeCallable],\n",
    "    producers: Dict[str, ProduceCallable],\n",
    "    kafka_brokers: KafkaBrokers,\n",
    "    kafka_service_info: KafkaServiceInfo,\n",
    "    asyncapi_path: Union[Path, str],\n",
    "    force_rebuild: bool = True,\n",
    ") -> None:\n",
    "    \"\"\"Exports the AsyncAPI specification and documentation to the given path.\n",
    "\n",
    "    Args:\n",
    "        consumers: Dictionary of consumer functions, where the keys are the channel names and the values are the consumer functions.\n",
    "        producers: Dictionary of producer functions, where the keys are the channel names and the values are the producer functions.\n",
    "        kafka_brokers: KafkaBrokers object representing the Kafka brokers configuration.\n",
    "        kafka_service_info: KafkaServiceInfo object representing the Kafka service info configuration.\n",
    "        asyncapi_path: Path or string representing the base path where the specification and documentation will be exported.\n",
    "        force_rebuild: Boolean indicating whether to force a rebuild of the specification file even if it already exists.\n",
    "    \"\"\"\n",
    "    # generate spec file\n",
    "    spec_path = Path(asyncapi_path) / \"spec\" / \"asyncapi.yml\"\n",
    "    is_spec_built = _generate_async_spec(\n",
    "        consumers=consumers,\n",
    "        producers=producers,\n",
    "        kafka_brokers=kafka_brokers,\n",
    "        kafka_service_info=kafka_service_info,\n",
    "        spec_path=spec_path,\n",
    "        force_rebuild=force_rebuild,\n",
    "    )\n",
    "\n",
    "    # generate docs folder\n",
    "    docs_path = Path(asyncapi_path) / \"docs\"\n",
    "\n",
    "    if not is_spec_built and docs_path.exists():\n",
    "        logger.info(\n",
    "            f\"Skipping generating async documentation in '{docs_path.resolve()}'\"\n",
    "        )\n",
    "        return\n",
    "\n",
    "    _generate_async_docs(\n",
    "        spec_path=spec_path,\n",
    "        docs_path=docs_path,\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2403e140",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'msg_url': {'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}, 'email': 'agent-007@sis.gov.uk'}\n",
      "{'info': {'mobile': '+385987654321', 'name': 'James Bond'}, 'url': 'https://sis.gov.uk/agents/007'}\n",
      "[INFO] __main__: Old async specifications at '/tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml' does not exist.\n",
      "[INFO] __main__: New async specifications generated at: '/tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml'\n",
      "[INFO] __main__: Async docs generated at '/tmp/003_AsyncAPI/asyncapi/docs'\n",
      "[INFO] __main__: Output of '$ npx -y -p @asyncapi/generator ag /tmp/003_AsyncAPI/asyncapi/spec/asyncapi.yml @asyncapi/html-template -o /tmp/003_AsyncAPI/asyncapi/docs --force-write'\u001b[32m\n",
      "\n",
      "Done! ✨\u001b[0m\n",
      "\u001b[33mCheck out your shiny new generated files at \u001b[0m\u001b[35m/tmp/003_AsyncAPI/asyncapi/docs\u001b[0m\u001b[33m.\u001b[0m\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "with tempfile.TemporaryDirectory() as d:\n",
    "    try:\n",
    "        asyncapi_path = Path(d).parent / \"003_AsyncAPI\" / \"asyncapi\"\n",
    "        if asyncapi_path.exists():\n",
    "            shutil.rmtree(asyncapi_path)\n",
    "\n",
    "        export_async_spec(\n",
    "            consumers=consumers,\n",
    "            producers=producers,\n",
    "            kafka_brokers=kafka_brokers,\n",
    "            kafka_service_info=kafka_service_info,\n",
    "            asyncapi_path=asyncapi_path,\n",
    "            force_rebuild=False,\n",
    "        )\n",
    "        #         !ls -al {asyncapi_path}\n",
    "        assert (Path(asyncapi_path) / \"spec\" / \"asyncapi.yml\").exists()\n",
    "        assert (Path(asyncapi_path) / \"docs\" / \"index.html\").exists()\n",
    "    finally:\n",
    "        shutil.rmtree(asyncapi_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75939f55",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
